#include "Lexer.h"
#include "Error.h"
#include <cmath>
#include <iostream>

using namespace std;
using namespace fileIO;


static char minuscule(char c) {
  if ((c<='Z') && (c>='A')) return (c-'A')+'a';
  else return c;
}

// ************************************************
// LEXER
// ************************************************

Lexer::Lexer(std::string nom,int mode) {
  commentLineId="//";
  separator="    .\0";
  separator[0]=char(10);
  separator[1]=char(13);
  separator[2]=char(9);

  the_end=false;
  isExist=true;
  endline=((mode & EOL_TOKEN)!=0);
  nb_ligne=0;
  sensitive=((mode & CASE_SENSITIVE)!=0);
  _filename=nom;
  f.open(nom.c_str(),fstream::binary | fstream::in);
  if (!f.is_open()) isExist=false;
  else skipCar();
}

bool Lexer::isOk() {
  return isExist;
}

void Lexer::close() {
  f.close();
}

bool Lexer::isOpen() {
  return isExist;
}

string Lexer::filename() {
  return _filename;
}

void Lexer::setSeparator(string sep) {
  separator=separator+sep;
}

Lexer::~Lexer() {
//  printf("nb lignes analysee fichier = %d\n",nb_ligne);
  f.close();
}

bool Lexer::isEnd() {
  return f.eof();
}

bool Lexer::isEol() {
  if (isEnd()) return true;
  if (le_car==char(13)) {
    skipCar();
  }
  if (le_car==char(10))
    return true;
  else return false;
}

void Lexer::skipEol() {
  if (le_car==char(13))
    skipCar();
  if (le_car==char(10))
    skipCar();
}


void Lexer::passerEspaceLigne() {
  if (endline)
    while (!isEnd() && ((le_car==' ') || (le_car=='\t'))) {
      skipCar();
    }
  else
    while (!isEnd() && ((le_car==' ') || (le_car=='\t') || isEol())) {
      if (isEol()) {
        nb_ligne++;
        skipEol();
      }
      else
        skipCar();

    }

}

void Lexer::passerEspace() {
  while (!isEnd() && ((le_car==' ') || (le_car=='\t'))) {
    skipCar();
  }
}



void Lexer::lireLigne() {
  la_chaine="";
  while (!isEol()) {
    skipCar();
    la_chaine+=le_car;
  }
  skipCar();
}

bool Lexer::skipCar() {
  /*
  if (isEnd() || !isOpen())
    error("cant read the character (file ended or not open)",__LINE__,__FILE__);
    */

  f.read(&le_car,1);
  if (f.eof()) {
    the_end=true;
    le_car='\0';
    return false;
  }
  if (!sensitive) {
    le_car=minuscule(le_car);
  }
  return true;
}

bool Lexer::readString(const string &s) {
  bool ok=true;
  unsigned int i=0;
  while (ok && (i<s.length())) {
    if (isEol()) skipCar();
  else {
    ok=(le_car==s[i]);
    i++;
    skipCar();
  }
  }
  if (!ok) error("String "+s+" attendu !",__LINE__,__FILE__);
  return ok;
}


bool Lexer::isDigit() {
  return (((le_car<='9') && (le_car>='0')));
}

bool Lexer::isLineComment() {
  size_t found;
  found=la_chaine.find(commentLineId,0);
  return (found!=string::npos);
}

void Lexer::setCommentLine(string s) {
  commentLineId=s;
}

bool Lexer::isSeparator() {
  unsigned int ind=0;
  bool trouve=false;
  while ((ind<separator.length() && !trouve)) {
    if (le_car==separator[ind]) trouve=true;
    ind++;
  }
  return trouve;

}


bool Lexer::skipInteger() {
  le_int=0;
  int signe=1;
  if (le_car=='-') signe=-1;
  if ((le_car=='-') || (le_car=='+')) skipCar();
  if (!isDigit()) {
    la_chaine+="-"+le_car;
    le_token=INCONNU;
    return false;
  }
  while (isDigit()) {
    le_int=le_int*10+((int)(le_car-'0'));
    skipCar();
  }
  le_int=le_int*signe;
  return true;
}


bool Lexer::skipNumber() {
  le_reel=0.0;
  le_token=CARDINAL;
  double signe=1.0;
  if (le_car=='-') {
    signe=-1.0;
    le_token=INTEGER;
  }
  if (isDigit() || (le_car=='-') || (le_car=='+')) {
    bool ok=skipInteger();
    if (!ok) return false;
  }
  if (le_car=='.') {
    double frac,puis;
    frac=0.0;
    puis=1;
    le_token=DOUBLE;
    skipCar();
    le_reel=(double)le_int;
    while (isDigit()) {
      puis=puis/10.0;
      le_reel=le_reel+signe*(double)((int)(le_car-'0'))*puis;
      skipCar();
    }
  }
  if (le_car=='e') {
    le_token=DOUBLE;
    skipCar();
    skipInteger();
    le_reel=le_reel*pow(10,float(le_int));
  }
  return true;
}





void Lexer::check() {
  switch(le_token) {
  case INCONNU:
    cout << "sans semantique = " << la_chaine << "(special = " << int(la_chaine[0]) << ")" << endl;
    break;
  case DOUBLE:
    cout << "reel = " << le_reel << endl;
    break;
  case CARDINAL:
    cout << "naturel = "<< le_int << endl;
    break;
  case INTEGER:
    cout << "integer = "<< le_int << endl;
    break;
  case FIN:
    cout << "FIN" << endl ;
    break;
  case LINE_COMMENT:
    cout << "comment = " << la_chaine << endl;
    break;
  case P_EOL:
    cout << "end of line" << endl;
    break;
  case CHAINE:
    cout << "CHAINE = " << la_chaine << endl;
    break;

  default:
    cout << "keyword : " << la_chaine << endl;
  }
}


void Lexer::skipToken() {
  la_chaine="";
  le_token=INCONNU;
  if (isEnd()) {
    le_token=FIN;
    goto fin;
  }
  passerEspaceLigne();
  if (isEnd()) {
    le_token=FIN;
    goto fin;
  }
  if (isEol()) {
    nb_ligne++;
    if (endline) le_token=P_EOL;
    else le_token=INCONNU;
    skipEol();
    goto fin;
  }
  if (isSeparator()) {
    la_chaine+=le_car;
    le_token=INCONNU;
    skipCar();
//    cout << "Separateur = " << la_chaine << endl;
    goto fin;
  }
  if ((isDigit()) || (le_car=='.') || (le_car=='+') || (le_car=='-')) {
    if (skipNumber()) {
      goto fin;
    }
  }
  if (le_car=='"') {
    skipCar();
    while (le_car!='"') {
      if (isEnd()) error("Lecture chaine constante non terminée\n",__LINE__,__FILE__);
      la_chaine+=le_car;
      skipCar();
    }
    skipCar();
    le_token=CHAINE;
    goto fin;
  }
  while (!isSeparator()) {
    la_chaine+=le_car;
    skipCar();
  }
  if (isLineComment()) {
    lireLigne();
    le_comment=la_chaine;
    la_chaine="";
    le_token=LINE_COMMENT;
  }
fin:
  return;
}

void Lexer::skipMot() {
  la_chaine="";
  le_token=INCONNU;
  passerEspaceLigne();
  if (isEnd()) {
    le_token=FIN;
    goto fin;
  }
  if (isEol()) {
    nb_ligne++;
    if (endline) le_token=P_EOL;
    else le_token=INCONNU;
    skipEol();
    goto fin;
  }
  if ((le_car==' ') || (le_car==char(13)) || (le_car==char(10))) {
    la_chaine+=le_car;
    le_token=INCONNU;
    skipCar();
    goto fin;
  }
  if (le_car=='"') {
    skipCar();
    while (le_car!='"') {
      if (isEnd()) error("Lecture chaine constante non terminée\n",__LINE__,__FILE__);
      la_chaine+=le_car;
      skipCar();
    }
    skipCar();
    le_token=CHAINE;
    goto fin;
  }
  while ((le_car!=' ') && (le_car!=char(13)) && (le_car!=char(10))) {
    la_chaine+=le_car;
    skipCar();
  }
fin:
  return;

}

void Lexer::skipMotSameLine() {
  la_chaine="";
  le_token=INCONNU;
  passerEspace();
  if (isEnd()) {
    le_token=FIN;
    goto fin;
  }
  if (isEol()) {
    nb_ligne++;
    le_token=P_EOL;
    skipEol();
    goto fin;
  }
  if ((le_car==' ') || (le_car==char(13)) || (le_car==char(10))) {
    la_chaine+=le_car;
    le_token=INCONNU;
    skipCar();
    goto fin;
  }
  if (le_car=='"') {
    skipCar();
    while (le_car!='"') {
      if (isEnd()) error("Lecture chaine constante non terminée\n",__LINE__,__FILE__);
      la_chaine+=le_car;
      skipCar();
    }
    skipCar();
    le_token=CHAINE;
    goto fin;
  }
  while ((le_car!=' ') && (le_car!=char(13)) && (le_car!=char(10))) {
    la_chaine+=le_car;
    skipCar();
  }
fin:
  return;

}
